/* 
 * Copyright (c) 2009 Martin Fuchs
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * http://creativecommons.org/licenses/LGPL/2.1/
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Thanks a lot to Karsten Schmidt, who wrote the required toxiclibscore.
 * 
 * Triangle intersection calculations based on jMonkeyEngine intersection class:
 * author Mark Powell, Intersection.java 4131 2009-03-19
 * Copyright (c) 2003-2009 jMonkeyEngine
 * UIntersection.java for details.
 */

package unwrap;

import java.util.ArrayList;

import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PFont;
import processing.core.PGraphics;
import toxi.geom.Vec3D;

/**
 * the main class
 */
public class Unwrap {

	/**
	 * optional clip-renderer for assembling
	 */
	public static UClip clip = new UClip();

	static PGraphics g;
	static PFont font;

	/**
	 * arraylist with the required three-dimensional triangles
	 */
	public ArrayList<UTriangle> triangles;
	/**
	 * arraylist with the required three-dimensional points
	 */
	public ArrayList<UPoint> points;

	private ArrayList<UPoint> tempP;
	private int tempPID;

	/**
	 * Constructs a new unwrapper WITHOUT font. You'll not be able to draw
	 * labels unless font is set afterwards.
	 * 
	 * @param theParent
	 */
	public Unwrap(PApplet theParent) {
		this(theParent, null);
	}

	/**
	 * Constructs a new unwrapper WITH font.
	 * 
	 * @param theParent
	 * @param font
	 *            name of the font to be used for labels
	 *            createFont(name,size)
	 */
	public Unwrap(PApplet theParent, PFont font) {
		this(theParent, font, new UTriangle[0], new UPoint[0]);
	}

	/**
	 * Constructs a new unwrapper WITH font and adds manually triangles and triangle points.
	 * 
	 */
	public Unwrap(PApplet theParent, PFont theFont, UTriangle[] _t,
			UPoint[] _points) {
		g = theParent.g;
		font = theFont;


		triangles = new ArrayList<UTriangle>();
		for (UTriangle tri : _t) {
			triangles.add(tri);
		}
		points = new ArrayList<UPoint>();
		for (UPoint pt : _points) {
			points.add(pt);
		}
		update();
		setTriangleNeighbors();
	}

	/**
	 * create a triangleStrip, similar to processing beginShape(TRIANGLE_STRIP).
	 */
	public void beginStrip() {
		tempP = new ArrayList<UPoint>();
		tempPID = 0;
	}

	/**
	 * add a point to the strip, similar to processing vertex(x,y,z).
	 */
	public void addPoint(Vec3D v) {
		addPoint(v.x, v.y, v.z);
	}

	/**
	 * add a point to the strip, similar to processing vertex(x,y,z).
	 */
	public void addPoint(float x, float y, float z) {
		tempP.add(new UPoint(x, y, z, tempPID++));
	}

	/**
	 * end the trianglestrip, similar to processing endShape().
	 */
	public void endStrip() {
		endStrip(0);
	}

	/**
	 * end and close the trianglestrip, similar to processing endShape(CLOSE).
	 */
	public void endStrip(int end) {
		if (tempP.size() < 3)
			return;
		points = tempP;
		int tID = triangles.size();
		int numP = points.size() - 2;
		for (int i = 0; i < numP; i++) {
			if (i % 2 == 0)
				triangles.add(new UTriangle(points.get(i), points.get(i + 1),
						points.get(i + 2), tID++));
			else
				triangles.add(new UTriangle(points.get(i), points.get(i + 2),
						points.get(i + 1), tID++));
		}

		if (end == PConstants.CLOSE) {
			triangles.add(new UTriangle(points.get(0), points.get(numP), points
					.get(numP + 1), tID++));
			triangles.add(new UTriangle(points.get(0), points.get(numP + 1),
					points.get(1), tID));
		}

		setTriangleNeighbors();
	}

	/**
	 * draw the triangles in 3d.
	 */
	public void draw3d() {
		for (UTriangle tri : triangles) {
			tri.draw3d();
		}
	}

	/**
	 * draw the triangle normals with default length.
	 */
	public void drawNormals() {
		for (UTriangle tri : triangles) {
			tri.drawNormal();
		}
	}

	/**
	 * update the point coordinates.
	 */
	public void update() {
		for (UTriangle tri : triangles) {
			tri.updatePoints(points);
			tri.update();
		}
	}

	/**
	 * calculate the triangle neighbors.
	 */
	private void setTriangleNeighbors() {
		USide[] s = new USide[triangles.size() * 3];
		int sidecount = 0;
		for (UTriangle tri : triangles) {
			s[sidecount++] = tri.getBC();
			s[sidecount++] = tri.getCA();
			s[sidecount++] = tri.getAB();
		}

		int i = 0;
		for (UTriangle tri : triangles) {
			compareSide(tri.getBC(), s, i);
			compareSide(tri.getCA(), s, i);
			compareSide(tri.getAB(), s, i);
			i++;
		}
	}

	/**
	 * compare the triangle sides.
	 */
	private void compareSide(USide n, USide[] s, int i) {
		for (int j = 0; j < s.length; j++) {
			if ((n.p1 == s[j].p1 && n.p2 == s[j].p2)
					|| (n.p1 == s[j].p2 && n.p2 == s[j].p1)) {
				if (s[j].triangle.id != i) {
					n.neighbor = s[j].triangle;
					if (s[j].sideState == false) {
						n.sideState = true;
					}
					break;
				}
			}
		}
	}
	
	// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////INTERSECTION

	private boolean intersection = false;
	
	/**
	 * draw triangles in 3d and display intersections
	 */
	public void display3dIntersection() {
		intersection = false;
		for (UTriangle tri : triangles) {
			if (triangle3dIntersection(tri) == true) {
				g.stroke(200, 0, 0);
				g.fill(250, 0, 0);
				tri.draw3d();
				intersection = true;
			} else {
				g.stroke(0);
				// g.noFill();
				g.fill(180);
				tri.draw3d();
			}
		}
	}

	/**
	 * find 3d triangle intersection with the given triangle
	 */
	public boolean intersectionStatus() {
		return intersection;
	}

	/**
	 * find 3d triangle intersection with the given triangle
	 */
	public boolean triangle3dIntersection(UTriangle _t) {
		for (UTriangle tri : triangles) {
			if (_t != tri && _t.findIntersection(tri) == true)
				return true;
		}
		return false;
	}
}
